package com.tianhao.luo.uitls;

import com.tianhao.luo.config.Configuration;
import com.tianhao.luo.domain.entity.HfleFile;
import com.tianhao.luo.domain.entity.HiamUser;
import io.minio.MinioClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.UUID;

/**
 * @ClassName: FileUtil
 * @Description: 文件客户端
 * @author: tianhao.luo@hand-china.com
 * @date: 2020/8/6  22:17
 */
public class FileClient implements FileOperator<HfleFile, HiamUser> {
    private final Logger LOGGER = LoggerFactory.getLogger(FileClient.class);

    private Configuration configuration;

    public FileClient(Configuration configuration) {
        this.configuration = configuration;
    }

    private String BUCKET = null;
    private String ENDPOINT = null;
    private String NGINX = null;
    private String ALGORITHM = null;
    private String CONTENT_LENGTH = null;
    private String CONTENT_DISPOSITION = null;
    private String CHARSET_NAME = null;

    private MinioClient minioClient = null;

    public void init() {
        try {
            Configuration.Minio minio = configuration.getMinio();
            Configuration.ServletResponse servletResponse = configuration.getServletResponse();
            BUCKET = minio.getBucket();
            ENDPOINT = minio.getEndPoint();
            NGINX = configuration.getNginx().getLocaltion();
            ALGORITHM = minio.getAlgorithm();
            CONTENT_LENGTH = servletResponse.getHeader().getLength();
            CONTENT_DISPOSITION = servletResponse.getHeader().getDisposition();
            CHARSET_NAME = servletResponse.getFile().getCharsetName();

            minioClient = new MinioClient(ENDPOINT, minio.getAccessKey(), minio.getSecretKey());
            if (!minioClient.bucketExists(BUCKET)) {
                minioClient.makeBucket(BUCKET);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public HfleFile uploadFile(HiamUser hiamUser, MultipartFile file) {
        // 使用putObject上传一个文件到存储桶中。
        String originalFilename = file.getOriginalFilename();
        Assert.isTrue(originalFilename != null, "上传文件为空");
        String postFix = getPostFix(originalFilename);
        String fileType = getFileType(postFix);
        UUID uuid = UUID.randomUUID();
        String objectName = uuid + postFix;
        BufferedInputStream bis = null;
        try {
            bis = new BufferedInputStream(file.getInputStream());
            fileType = getMinioFileType(fileType);
            minioClient.putObject(BUCKET, objectName, bis, fileType);
            bis.close();
            return getHfleFile(hiamUser, file, originalFilename, fileType, minioClient, uuid, objectName);
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
            return null;
        }
    }

    /**
     * 获取文件的后缀类型
     *
     * @param postFix 包括.的后缀
     * @return 单独的后缀字符串
     */
    private String getFileType(String postFix) {
        return postFix.substring(postFix.lastIndexOf('.') + 1);
    }

    /**
     * 获取文件后缀
     *
     * @param originalFilename 文件原始名称
     * @return 文件的最后一个.开始的字符串，包括.
     */
    private String getPostFix(String originalFilename) {
        return originalFilename.substring(originalFilename.lastIndexOf('.'));
    }

    /**
     * 生成HfleFile对象，用于持久化
     *
     * @param hiamUser         用户实体
     * @param file             接口传入的文件
     * @param originalFilename 文件原始名称
     * @param fileType         文件对应minio的类型
     * @param minioClient      minio客户端
     * @param uuid             文件的uuid字符串
     * @param objectName       文件保存在minio上的名称
     * @return HfleFile对象
     * @throws Exception 抛出异常
     */
    private HfleFile getHfleFile(HiamUser hiamUser, MultipartFile file, String originalFilename, String fileType, MinioClient minioClient, UUID uuid, String objectName) throws Exception {
        HfleFile hfleFile = new HfleFile();
        hfleFile.setAttachmentUuid(uuid.toString());
        hfleFile.setBucketName(BUCKET);
        hfleFile.setDirectory(BUCKET);
        hfleFile.setFileName(originalFilename);
        hfleFile.setFileSize(file.getSize());
        hfleFile.setFileType(fileType);
        hfleFile.setFileUrl(minioClient.getObjectUrl(BUCKET, objectName).replace(ENDPOINT, NGINX));
        hfleFile.setTenantId(hiamUser.getTenantId());
        hfleFile.setMd5(getFileMd5(file));
        return hfleFile;
    }


    /**
     * 获取文件在Minio中的文件类型
     *
     * @param fileType 文件原始类型
     * @return Minio中的类型
     */
    private String getMinioFileType(String fileType) {
        FileType[] values = FileType.values();
        for (FileType value : values) {
            if (value.getType().equals(fileType)) {
                fileType = value.getMinioType();
            }
        }
        return fileType;
    }

    /**
     * 获取md5标识，并赋给fileMd5字段，避免重复使用函数
     *
     * @param file 传入文件
     * @return md5标识字符串
     */
    @Override
    public String getFileMd5(MultipartFile file) {
        byte[] buffer = new byte[1024];
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(ALGORITHM);
            InputStream inputStream = file.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            int length = bis.read(buffer, 0, buffer.length);
            if (length > 0) {
                messageDigest.update(buffer);
                return new BigInteger(1, messageDigest.digest()).toString(16);
            }
            return null;
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
            return null;
        }
    }

    @Override
    public void downLoadFile(HfleFile hfleFile, HttpServletResponse httpServletResponse) {
        //构造出存放在minio中的objectName
        String type = hfleFile.getFileType().substring(hfleFile.getFileType().lastIndexOf('/') + 1);
        String objectName = hfleFile.getAttachmentUuid() + "." + type;

        try {
            //minio获取文件的输入流
            InputStream is = minioClient.getObject(BUCKET, objectName);
            //使用缓冲
            BufferedInputStream bis = new BufferedInputStream(is);
            //响应请求头设置
            httpServletResponse.setHeader(CONTENT_LENGTH, String.valueOf(hfleFile.getFileSize()));
            httpServletResponse.setHeader(CONTENT_DISPOSITION, "filename = " + decodeFileName(hfleFile));
            //获取输出流，用来下载文件
            OutputStream os = httpServletResponse.getOutputStream();
            BufferedOutputStream bos = new BufferedOutputStream(os);
            byte[] buff = new byte[1024];
            //记录文件读取的位置
            int len;
            while ((len = bis.read(buff)) != -1) {
                bos.write(buff, 0, len);
            }
            bos.flush();
            bis.close();
            os.close();
            bos.close();
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * 解决浏览器下载文件中文乱码
     * @param hfleFile 文件对象
     * @return 正常的名称
     * @throws Exception String创建异常
     */
    private String decodeFileName(HfleFile hfleFile) throws Exception {
        byte[] bytes = hfleFile.getFileName().getBytes(StandardCharsets.UTF_8);
        return new String(bytes, CHARSET_NAME);
    }
}
